package com.xiaoq.matrix.minipro.service.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import com.alibaba.dubbo.config.annotation.Reference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoq.RedisTemplate4JedisCluster;
import com.xiaoq.apis.user.IMerchantMgrApi;
import com.xiaoq.apis.user.MerchantMgrInfo4Vo;
import com.xiaoq.coms.util.JsonUtil;
import com.xiaoq.coms.util.MapUtil;
import com.xiaoq.matrix.minipro.MiniProApiException;
import com.xiaoq.matrix.minipro.entity.ShopMiniProEntity;
import com.xiaoq.matrix.minipro.entity.ShopMiniProVersionEntity;
import com.xiaoq.matrix.minipro.repository.impl.ShopMiniProRepositoryImpl;
import com.xiaoq.matrix.minipro.vo.MiniProAuditStatusResponse;
import com.xiaoq.matrix.minipro.vo.MiniProAuthorizerInfo;
import com.xiaoq.matrix.minipro.vo.MiniProBaseResponse;
import com.xiaoq.matrix.minipro.vo.MiniProCategory;
import com.xiaoq.matrix.minipro.vo.MiniProCategoryListResponse;
import com.xiaoq.matrix.minipro.vo.MiniProComponentToken;
import com.xiaoq.matrix.minipro.vo.MiniProDetailInfoVo;
import com.xiaoq.matrix.minipro.vo.MiniProDraftOrTemplateListResponse;
import com.xiaoq.matrix.minipro.vo.MiniProFunctionInfo;
import com.xiaoq.matrix.minipro.vo.MiniProPageListResponse;
import com.xiaoq.matrix.minipro.vo.MiniProPreAuthCode;
import com.xiaoq.matrix.minipro.vo.MiniProSubmitAuditResponse;
import com.xiaoq.web.DefaultMapper;
import com.xiaoq.web.HttpsRestTemplate;

import io.jsonwebtoken.lang.Collections;

@Service
public class MiniProServiceImpl {
	private static final Logger LOG = LoggerFactory.getLogger(MiniProServiceImpl.class);
	
	private final String KEY_PREFIX = "miniprosaa";
	private final String KEY_TICKET = "tickets";
	
	private final String KEY_TOKEN_OBJ = "tokenObjs";
	private final String FIELD_TOKEN = "tokens";
	private final String FIELD_EXPIRES = "expires";
	
	private final String KEY_PRE_AUTH_CODE_OBJ = "preCodeObjs";
	private final String FIELD_PRE_AUTH_CODE = "preCodes";
	
	
	@Value("${wechat.open.componentAppId}")
	private String appId;
	
	@Value("${wechat.open.componentSecret}")
	private String appSecret;
	
	@Value("${wechat.open.authRedirectUrl}")
	private String authRedirectUrl;
	
	
	@Value("${wechat.open.miniRedirectUrl}")
	private String miniRedirectUrl;
	
	
	@Autowired
	private HttpsRestTemplate rest;
	
    @Autowired
    private RedisTemplate4JedisCluster redis;
	
    @Reference(interfaceClass=IMerchantMgrApi.class, check=false, version="1.0.0")
    private IMerchantMgrApi mgrApi;
    
    @Autowired
    private ShopMiniProRepositoryImpl repo;
    
	public String getTicket() {
		return redis.get(KEY_PREFIX, KEY_TICKET);
	}
	
	
	/*
	 * 保存ticket到缓存
	 * 如果需要，更新access_token
	 * 这里直接不做刷新规则了容易出问题
	 */
	public void setTicket(String ticket) {
		LOG.info("微信发送的ticket为：" + ticket);
		redis.set(KEY_PREFIX, KEY_TICKET, ticket);
		// TODO 这个刷新规则得改一下
		LOG.info("到redis set 下一步了！");
		MiniProComponentToken tokenObj = this.getComponentTokenFromWeixin(ticket);
		this.setAccessTokenObj(tokenObj.getToken(), tokenObj.getExpiresIn());
		LOG.info("第三方accessToken为"+tokenObj);
		MiniProPreAuthCode preAuthCodeObj = this.getPreAuthCodeFromWeixin();
		this.setPreAuthCodeObj(preAuthCodeObj.getCode());
	}
	

	public void setAccessTokenObj(String token, long expires) {
		redis.set(KEY_PREFIX, KEY_TOKEN_OBJ, token);
//		redis.hsetAll(KEY_PREFIX, KEY_TOKEN_OBJ, MapUtil.buildMapKVGen(new Object[][] {
//			{FIELD_TOKEN, token},
//			{FIELD_EXPIRES, expires + ""}
//		}, String.class, String.class));
	}
	
//	public Map<String, String> getAccessTokenAsMap() {
//		return redis.hgetAll(KEY_PREFIX, KEY_TOKEN_OBJ);
//	}
	public boolean addSecret(String appId,String appSecret){
		return repo.addSecret(appId, appSecret);
		
	}
	
	public String getAccessTokenValue() {
		return redis.get(KEY_PREFIX, KEY_TOKEN_OBJ);
	}
		
	public void setPreAuthCodeObj(String code) {
//		redis.hsetAll(KEY_PREFIX, KEY_PRE_AUTH_CODE_OBJ, MapUtil.buildMapKVGen(new Object[][] {
//			{FIELD_PRE_AUTH_CODE, code},
//			{FIELD_EXPIRES, expires + ""}
//		}, String.class, String.class));
		LOG.info("设置的第三方的授权吗"+code);
		redis.set(KEY_PREFIX, KEY_PRE_AUTH_CODE_OBJ, code);
	}
	
//	public Map<String, String> getPreAuthCodeAsMap() {
//		return redis.hgetAll(KEY_PREFIX, KEY_PRE_AUTH_CODE_OBJ);
//	}
	
	public String getPreAuthCodeValue() {
		return redis.get(KEY_PREFIX, KEY_PRE_AUTH_CODE_OBJ);
	}

	public void saveAuthorizerInfo(String appId, MiniProAuthorizerInfo info, String userId,String type) {
		MerchantMgrInfo4Vo mgrVo = mgrApi.findMerMgr(userId);
		repo.saveAuthorizerInfo(appId, info, userId, mgrVo.getMerchantId(), mgrVo.getShopId(), mgrVo.getShopType(),type);
	}
	
	public MiniProAuthorizerInfo getAuthorizerInfo(String appId) {
		return repo.getAuthorizerInfoByAppId(appId);
	}
	
	public String getAuthorizerTokenValue(String appId) {
		return this.getAuthorizerInfo(appId).getAuthorizerAccessToken();
	}
	
	private HttpHeaders defaultHeaders() {
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
		return headers;
	}
	
	private String post(String url, Map<String, Object> body) {
//		HttpEntity<String> reqEntity = new HttpEntity<String>(null, defaultHeaders());
		HttpEntity<Map<String, Object>> reqEntity = new HttpEntity<>(body);
		String msg = "调用微信服务器发生未知错误";
		String respStr = null;
		try {
			respStr = rest.postForObject(new URI(url),
					reqEntity,
					String.class);
			LOG.info("返回结果json字符串："+respStr);
		} catch (Exception e) {
			LOG.error("发送post请求出错", e);
		} 
		if (respStr != null) {
			return respStr;
		}
		LOG.warn(msg);
		throw new MiniProApiException(-9999L, msg);
	}
	
	private String get(String url) {
		HttpEntity<String> reqEntity = new HttpEntity<String>(null, defaultHeaders());
		ResponseEntity<String> resp = null;
		String msg = "调用微信服务器发生未知错误";
		try {
			resp = rest.exchange(new URI(url),
					HttpMethod.GET,
					reqEntity,
					String.class);
		} catch (Exception e) {
			LOG.error("RestTemplate请求报错", e);
			
		} 
		if (resp != null && resp.getStatusCodeValue() == HttpStatus.OK.value()) {
			return resp.getBody();
		}
		LOG.warn(msg);
		throw new MiniProApiException(-9999L, msg);
	}


	
	/**
	 * 获取第三方平台的token
	 * @param ticket
	 * @return
	 */
	private MiniProComponentToken getComponentTokenFromWeixin(String ticket) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "component_appid", this.appId }, 
					{"component_appsecret", this.appSecret },
					{"component_verify_ticket", ticket}}, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/cgi-bin/component/api_component_token", body);
		LOG.info("第三方token:"+respStr);
		return JsonUtil.asObject(respStr, MiniProComponentToken.class);	
	}
	
	
	/**
	 * 获取第三方平台授权机制预授权码
	 * @return
	 */
	private MiniProPreAuthCode getPreAuthCodeFromWeixin() {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "component_appid", this.appId } }, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=" + this.getAccessTokenValue(),
				body);
		LOG.info("预授权码为："+respStr);
		return JsonUtil.asObject(respStr, MiniProPreAuthCode.class);	
	}
	
	public MiniProPreAuthCode getPreAuthCodeFromWeixinTest(String thirdToken) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "component_appid", this.appId } }, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=" + thirdToken,
				body);
		return JsonUtil.asObject(respStr, MiniProPreAuthCode.class);	
	}

//-------------------****************---------------------	
	/**
	 * 下面两个函数 使用授权码换取公众号的授权信息 并将其保存在数据库中
	 * @param authCode
	 * @return
	 */
	private MiniProAuthorizerInfo getAuthorizerInfoFromWeixin(String authCode) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "component_appid", this.appId }, { "authorization_code", authCode } }, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=" + this.getAccessTokenValue(),
				body);
		LOG.info("获取第三方token返回的结果！"+respStr);
		MiniProAuthorizerInfo info = null;
		try{
			Map<String, MiniProAuthorizerInfo> respMap = JsonUtil.asMapWithStringKey(respStr, MiniProAuthorizerInfo.class);
			info = respMap.get("authorization_info");
			
		}catch(Exception e){
			return info;
		}
		
//		MiniProAuthorizerInfo info = JsonUtil.asObject(respStr, MiniProAuthorizerInfo.class);
		return info;
	}

	/**
	 * 添加用户授权的token
	 * @param userId
	 * @param authCode
	 * @param type
	 * @return
	 */
	public MiniProAuthorizerInfo authCodeCallback(String userId, String authCode,String type) {
		// 换取授权信息并保存
		MiniProAuthorizerInfo info = this.getAuthorizerInfoFromWeixin(authCode);
		if(info==null){
			return info;
		}
		Integer errcode = info.getErrcode();
		LOG.info("errcode"+errcode);
		if(errcode==null){
			long expiresIn =  info.getExpiresIn();
			info.setExpiresIn((expiresIn-1000)*1000+System.currentTimeMillis());
			this.saveAuthorizerInfo(info.getAuthorizerAppId(), info, userId,type);
		}
		return info;
	}
	
	/**
	 * 获取小程序二维码
	 * @param shopId
	 * @param type
	 * @return
	 */
	public String getShopMiniProQrImage(String shopId,String type){
		ShopMiniProEntity shopMiniPro = repo.getByShopId(shopId, type);
		if(shopMiniPro!=null){
			return shopMiniPro.getQrImage();
		}
		return null;
	}
	/**
	 * 更新小程序二维码
	 * @param shopId
	 * @param type
	 * @param qrImage
	 * @return
	 */
	public boolean updateQrImage(String shopId,String type,String qrImage){
		ShopMiniProEntity shopMiniPro = repo.getByShopId(shopId, type);
		if(shopMiniPro!=null){
			shopMiniPro.setQrImage(qrImage);
			repo.saveShopMiniProEntity(shopMiniPro);
			return true;
		}
		
		return false;
	}
	
	/**
	 * 获取用户授权Token(添加缓存机制)
	 * @param shopId
	 * @return
	 */
	public String getAccessTokenByShopId(String shopId, String type) {
		ShopMiniProEntity shopMinPro = repo.getByShopId(shopId, type);
		LOG.info("刷新token机制这里shopId" + shopId + "shopMinProIDB" + shopMinPro);
		if (shopMinPro == null) {
			LOG.info("ShopMiniProEntity 获取空指针异常。");
			return null;
		}
		MiniProAuthorizerInfo authorizerIn = shopMinPro.getAuthorizerInfo();
		if (authorizerIn == null) {
			LOG.info("MiniProAuthorizerInfo 获取空指针异常。");
			return null;
		}
		String accessToken = null;
		long expiredIn = authorizerIn.getExpiresIn();
		boolean result = expiredIn-1000 < System.currentTimeMillis();
		if (result) {
			try {
				ShopMiniProEntity shopMinProIDB = refreshAccessToken(shopId, type);
				LOG.info("getAccessTokenByShopId---shopMinProIDB:" + shopMinProIDB);
				if (shopMinProIDB == null) {
					LOG.info("ShopMiniProEntity 获取空指针异常。");
					return null;
				}
				MiniProAuthorizerInfo authorizerInfo = shopMinProIDB.getAuthorizerInfo();
				if (authorizerInfo == null) {
					LOG.info("MiniProAuthorizerInfo 获取空指针异常。");
					return null;
				}
				accessToken = authorizerInfo.getAuthorizerAccessToken();
				LOG.info("调用的accessToken为！" + accessToken);

				return accessToken;
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
				return null;
			}
		}
		accessToken = authorizerIn.getAuthorizerAccessToken();
		return accessToken;

	}
	
	public ShopMiniProEntity refreshAccessToken(String shopId,String type){
		ShopMiniProEntity shopMinProIDB = repo.getByShopId(shopId,type);
		LOG.info("刷新token机制这里shopId"+shopId+"shopMinProIDB"+shopMinProIDB);
		if(shopMinProIDB==null){
			LOG.info("ShopMiniProEntity 获取空指针异常。");
			return null;
		}
		MiniProAuthorizerInfo authorizerInfo = shopMinProIDB.getAuthorizerInfo();
		if(authorizerInfo==null){
			LOG.info("MiniProAuthorizerInfo 获取空指针异常。");
			return null;
		}
		String authorizerAppId = authorizerInfo.getAuthorizerAppId();
		LOG.info("authorizerAppId---"+authorizerAppId);
		
		if(StringUtils.isEmpty(authorizerAppId)){
			LOG.info("authorizerAppId 获取空指针异常。");
			return null;
		}
		
		String authorizerRefreshToken = authorizerInfo.getAuthorizerRefreshToken();
		LOG.info("authorizerRefreshToken---"+authorizerRefreshToken);
		if(StringUtils.isEmpty(authorizerRefreshToken)){
			LOG.info("authorizerRefreshToken 获取空指针异常。");
			return null;
		}
		
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "component_appid", this.appId }, { "authorizer_appid", authorizerAppId }, { "authorizer_refresh_token", authorizerRefreshToken } }, String.class,
				Object.class);
		LOG.info("mapp--body---"+body.toString());
		String accessTokenr = authorizerInfo.getAuthorizerAccessToken();
		LOG.info("刷新Token之前accessToken为"+accessTokenr);
		String respStr = post("https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=" + this.getAccessTokenValue(),
				body);
		LOG.info("刷新Token之后的结果为："+respStr);
		MiniProAuthorizerInfo info = null;
		ObjectMapper objectMapper = new DefaultMapper();
		try {
			info = objectMapper.readValue(respStr, MiniProAuthorizerInfo.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		String errMessage = info.getErrmsg();
		
		if(StringUtils.isNotEmpty(errMessage)){
			return null;
		}
		
		String accessToken = info.getAuthorizerAccessToken();
		String refreshToken = info.getAuthorizerRefreshToken();
		long expiresIn = info.getExpiresIn();
		LOG.info("刷新refreshToken后的获取的信息"+info);
		if(StringUtils.isNotEmpty(accessToken)&&StringUtils.isNotEmpty(refreshToken)){
			authorizerInfo.setAuthorizerAccessToken(accessToken);
			authorizerInfo.setAuthorizerRefreshToken(refreshToken);
			authorizerInfo.setExpiresIn(expiresIn+System.currentTimeMillis());
			shopMinProIDB.setAuthorizerInfo(authorizerInfo);
			ShopMiniProEntity shopMinPro = repo.saveShopMiniProEntity(shopMinProIDB);
			return shopMinPro;
		}
		
	return shopMinProIDB;
		
	}
	
//-------------------****************---------------------	
	
	/**
	 * 
	 * 这个是一键发布小程序的流程
	 * @param userId
	 * @param appId
	 */
	public void audit(String userId, String appId) {
		// 获取token
		String token = this.getAuthorizerTokenValue(appId);
		// 获取类目
		MiniProCategoryListResponse categoryListResp = this.getCategoryListFromWeixin(token);
		
		// 提交审核
		List<MiniProCategory> items = categoryListResp.getCategoryList();
		for (MiniProCategory item : items) {
			item.setAddress("");  // TODO 怎么指定？
		}
		MiniProSubmitAuditResponse submitAuditResp = this.submitAudit2Weixin(token, items);
		
		// 版本版本信息
		ShopMiniProEntity e = repo.getByAppId(appId);
		ShopMiniProVersionEntity version = e.getLastVersion();
		if (version == null) {
			version = new ShopMiniProVersionEntity();
		}
		version.setAuditId(submitAuditResp.getAuditId());
		version.setAuditStatus(-99L); // 审核中
		version.setSubmitAuditTime(System.currentTimeMillis());
		version.setReleaseStatus(-100L); // 等待发布
		
		repo.saveLasterVersion(appId, version);
		
		// 更新二维码
		String qrcode = this.getQRCodeFromWeixin(token);
		if (qrcode != null) {
			repo.updateQrCode(appId, qrcode);
		}
	}
	
	public void receivedAuditFail(String appId, String reason) {
		ShopMiniProEntity e = repo.getByAppId(appId);
		ShopMiniProVersionEntity version = e.getLastVersion();
		version.setAuditStatus(-1L);
		version.setAuditMemo(reason);
		version.setAuditTime(System.currentTimeMillis());
		repo.saveLasterVersion(appId, version);
	}

	
	
	 

	public void receivedAuditSuccess(String appId) {
		ShopMiniProEntity e = repo.getByAppId(appId);
		ShopMiniProVersionEntity version = e.getLastVersion();
		version.setAuditStatus(0L);
		version.setAuditMemo(null);
		version.setAuditTime(System.currentTimeMillis());
		repo.saveLasterVersion(appId, version);
		
		// TODO 自动发布？
		String token = this.getAuthorizerTokenValue(appId);
		MiniProBaseResponse wxResp = this.release2Weixin(token);
		Long errCode = wxResp.getErrCode();
		if (errCode.equals(0)) {
			version.setReleaseStatus(0L);
			version.setReleaseTime(System.currentTimeMillis());
			version.setReleaseMemo(null);
		} else {
			version.setReleaseStatus(errCode);
			version.setReleaseTime(System.currentTimeMillis());
			version.setReleaseMemo(wxResp.getErrMsg());
		}
		
		repo.saveLasterVersion(appId, version);
	}
	

	/**
	 * 代替小程序修改服务器域名 ---有的域名添加不上的原因就是在下面代码中添加的域名一定添加要在开放平台的服务器域名设置中
	 * @param token
	 * @return
	 */
	public MiniProBaseResponse saveDomainConf2Weixin(String token) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "action", "set" }, 
					{ "requestdomain", new String[] {"https://order.xiaoq.online","https://user.xiaoq.online","https://matrix.xiaoq.online","https://sites.xiaoq.online","https://restapi.amap.com","https://tsn.baidu.com","https://openapi.baidu.com"} },
					{ "wsrequestdomain", new String[] {} },
					{ "uploaddomain", new String[] {"https://order.xiaoq.online","https://user.xiaoq.online","https://matrix.xiaoq.online","https://sites.xiaoq.online","https://www.xiaoq.online"} },
					{ "downloaddomain", new String[] {"https://order.xiaoq.online","https://user.xiaoq.online","https://matrix.xiaoq.online","https://sites.xiaoq.online","https://www.xiaoq.onlne"} }}, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/wxa/modify_domain?access_token=" + token,
				body);
		LOG.info("修改授权小程序服务器地址返回结果"+respStr);
		return JsonUtil.asObject(respStr, MiniProBaseResponse.class);
	}
	
	public MiniProBaseResponse saveWebviewConf2Weixin(String token) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { { "action", "set" }, 
					{ "webviewdomain", new String[] {"https://www.xiaoq.online"} }}, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/wxa/setwebviewdomain?access_token=" + token,
				body);
		LOG.info("修改授权小程序业务域名返回结果"+respStr);
		return JsonUtil.asObject(respStr, MiniProBaseResponse.class);
	}

	/**
	 * 获取小程序服务器域名
	 * 
	 * @param token
	 */
	public String getDomainConfFromWeixin(String token) {
		RestTemplate rest = new RestTemplate();
		Map<String, Object> body = MapUtil.buildMapKVGen(new Object[][] { { "action", "get" } }, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/wxa/modify_domain?access_token=" + token, body);
		return respStr;
	}
	

	//***********************代码管理*************************//
	//这里有一点要特别注意ext.json 中extappid 一定要赋值给对应的收授权的小程序的appid 这个授权厚是可以从库里面拿出来的
	public MiniProBaseResponse commitCode2Weixin(String token, Long templateId, String extJson, String version, String desc) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { 
					{ "template_id", templateId },
					{ "ext_json", extJson },
					{ "user_version", version },
					{ "user_desc", desc }}, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/wxa/commit?access_token=" + token, body);
		return JsonUtil.asObject(respStr, MiniProBaseResponse.class);
	}

	/**
	 * 2、获取体验小程序的体验二维码
	 * @param token
	 * @return
	 */
	public String getQRCodeFromWeixin(String token) {
		//RestTemplate restTemplate = new RestTemplate();
		//restTemplate.getMessageConverters().add(
		//		new ByteArrayHttpMessageConverter());
		
		HttpHeaders headers = new HttpHeaders();
		headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
		
		HttpEntity<String> entity = new HttpEntity<String>(headers);
		
		ResponseEntity<byte[]> respObj = rest.exchange("https://api.weixin.qq.com/wxa/get_qrcode?access_token=" + token,
				HttpMethod.GET, entity, byte[].class, "1");
		
		if (respObj.getStatusCode() == HttpStatus.OK && MediaType.IMAGE_JPEG.equals(respObj.getHeaders().getContentType())) {
			byte[] body = respObj.getBody();
//			return Base64.encodeBase64URLSafeString(body);
			return Base64.encodeBase64String(body);
		}
		return null;
	}


	/**
	 * 添加体验二维码的体验者
	 * @param token
	 * @param wechatid
	 * @return
	 */
	public  MiniProBaseResponse addExperiencer(String token, String wechatid) {
		Map<String, Object> body = new HashMap<String, Object>();
		body.put("wechatid", wechatid);
		String resp = post("https://api.weixin.qq.com/wxa/bind_tester?access_token=" + token, body);
		return JsonUtil.asObject(resp, MiniProBaseResponse.class);
	}
	
//----------------------***********调教发布之前的准备或者提价发布****************------------------	


	/**
	 * 获取授权小程序帐号的可选类目
	 * @param token
	 * @return
	 */
	public MiniProCategoryListResponse getCategoryListFromWeixin(String token) {
		String respStr = get("https://api.weixin.qq.com/wxa/get_category?access_token=" + token);
		if (respStr == null) {
			return null;
		}
		LOG.info("获取小程序可选类目结果"+respStr);
		MiniProCategoryListResponse respObj = JsonUtil.asObject(respStr, MiniProCategoryListResponse.class);
		return respObj;
	}

	/**
	 * 获取小程序的第三方提交代码的页面配置（仅供第三方开发者代小程序调用）
	 * @param token
	 * @return
	 */
	public MiniProPageListResponse getPageConfFromWeixin(String token) {
		String respStr = get("https://api.weixin.qq.com/wxa/get_page?access_token=" + token);
		if (respStr == null) {
			return null;
		}
		LOG.info("获取小程序页面配置项结果"+respStr);
		MiniProPageListResponse miniProPageObj =  JsonUtil.asObject(respStr, MiniProPageListResponse.class);
		return miniProPageObj;
	}
	
/*
	5、将第三方提交的代码包提交审核（仅供第三方开发者代小程序调用）
	请求方式: POST（请使用https协议）
	url: https://api.weixin.qq.com/wxa/submit_audit?access_token=TOKEN
	
	POST数据示例:
	{
		"item_list": [
			{
				"address":"index",
				"tag":"学习 生活",
				"first_class": "文娱",
				"second_class": "资讯",
	            "first_id":1,
	            "second_id":2,
				"title": "首页"
			}
			{
				"address":"page/logs/logs",
				"tag":"学习 工作",
				"first_class": "教育",
				"second_class": "学历教育",
				"third_class": "高等",
	            "first_id":3,
	            "second_id":4,
	            "third_id":5,
				"title": "日志"
			}
		]
	}
	
	
	参数说明
	access_token 	
	请使用第三方平台获取到的该小程序授权的authorizer_access_token
	item_list	提交审核项的一个列表（至少填写1项，至多填写5项）
	address	小程序的页面，可通过“获取小程序的第三方提交代码的页面配置”接口获得
	tag	小程序的标签，多个标签用空格分隔，标签不能多于10个，标签长度不超过20
	first_class	一级类目名称，可通过“获取授权小程序帐号的可选类目”接口获得
	second_class	二级类目(同上)                                                                               
	third_class	三级类目(同上)
	first_id	一级类目的ID，可通过“获取授权小程序帐号的可选类目”接口获得
	second_id	二级类目的ID(同上)
	third_id	三级类目的ID(同上)
	title	小程序页面的标题,标题长度不超过32
	
	
	返回说明（正常时返回的json示例）：
	{
		"errcode":0,
		"errmsg":"ok",
		"auditid":1234567
	}
	
	
	返回参数说明：
	auditid	审核编号
	
	错误码说明：
	返回码	说明
	-1	 系统繁忙
	86000 	 不是由第三方代小程序进行调用
	86001 	  不存在第三方的已经提交的代码
	85006 	  标签格式错误
	85007 	  页面路径错误
	85008 	  类目填写错误
	85009 	  已经有正在审核的版本
	85010	  item_list有项目为空
	85011 	  标题填写错误
	85023 	  审核列表填写的项目数不在1-5以内
	85077	小程序类目信息失效（类目中含有官方下架的类目，请重新选择类目）
	86002	  小程序还未设置昵称、头像、简介。请先设置完后再重新提交。

*/
	public MiniProSubmitAuditResponse submitAudit2Weixin(String token, List<MiniProCategory> items) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { 
					{ "item_list", items }
				}, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/wxa/submit_audit?access_token=" + token, body);
		return JsonUtil.asObject(respStr, MiniProSubmitAuditResponse.class);
		
	}		
	
/*	
	7、查询某个指定版本的审核状态（仅供第三方代小程序调用）
	请求方式: POST（请使用https协议）
	url: https://api.weixin.qq.com/wxa/get_auditstatus?access_token=TOKEN
	
	POST数据示例:
	{
	    "auditid":1234567
	}
	
	参数说明:
	access_token	请使用第三方平台获取到的该小程序授权的authorizer_access_token
	auditid	提交审核时获得的审核id
	
	返回说明（正常时返回的json示例）：
	{
		"errcode":0,
		"errmsg","ok",
		"status"：1,
	    "reason:"帐号信息不合规范"
	}
	
	返回参数说明：
	status	审核状态，其中0为审核成功，1为审核失败，2为审核中
	reason	当status=1，审核被拒绝时，返回的拒绝原因
	
	
	错误码说明：
	-1	系统繁忙
	86000 	不是由第三方代小程序进行调用
	86001 	 不存在第三方的已经提交的代码
	85012 	 无效的审核id

*/
	public MiniProAuditStatusResponse getAuditStatusFromWeixin(String token, Long auditId) {
		Map<String, Object> body = MapUtil.buildMapKVGen(
				new Object[][] { 
					{ "auditid", auditId }
				}, String.class,
				Object.class);

		String respStr = post("https://api.weixin.qq.com/wxa/get_auditstatus?access_token=" + token, body);
		return JsonUtil.asObject(respStr, MiniProAuditStatusResponse.class);
	}

/*
	8、查询最新一次提交的审核状态（仅供第三方代小程序调用）
	请求方式: GET（请使用https协议）
	url: https://api.weixin.qq.com/wxa/get_latest_auditstatus?access_token=TOKEN
	参数说明:
	access_token  请使用第三方平台获取到的该小程序授权的authorizer_access_token
	
	返回说明（正常时返回的json示例）：
	{
		"errcode":0,
		"errmsg","ok",
	    "auditid","1234567",
		"status"：1,
	    "reason":"帐号信息不合规范"
	}
	
	返回参数说明：
	auditid	最新的审核ID
	status	审核状态，其中0为审核成功，1为审核失败，2为审核中
	reason	当status=1，审核被拒绝时，返回的拒绝原因
	
	错误码说明：
	-1	系统繁忙
	86000 	不是由第三方代小程序进行调用
	86001 	 不存在第三方的已经提交的代码
	85012 	 无效的审核id

*/
	public MiniProAuditStatusResponse getLastAuditStatusFromWeixin(String token) {
		                      
		String respStr = get("https://api.weixin.qq.com/wxa/get_latest_auditstatus?access_token=" + token);
		return JsonUtil.asObject(respStr, MiniProAuditStatusResponse.class);
	}
	
/*	
	9、发布已通过审核的小程序（仅供第三方代小程序调用）
	请求方式: POST（请使用https协议）
	https://api.weixin.qq.com/wxa/release?access_token=TOKEN
	
	POST数据示例:
		{ }
	
	参数说明：
	请填写空的数据包，POST的json数据包为空即可。
	
	返回说明（正常时返回的json示例）：
	{
		"errcode":0,
		"errmsg":"ok",
	}
	
	错误码说明：
	-1	系统繁忙
	85019 	没有审核版本
	85020 	审核状态未满足发布

*/
	public MiniProBaseResponse release2Weixin(String token) {
//		Map<String, Object> body = MapUtil.buildMapKVGen(
//				new Object[][] { 
//					{ ,}
//				}, String.class,
//				Object.class);
//		LOG.info(body.toString());
//		String respStr = post("https://api.weixin.qq.com/wxa/release?access_token=" + token, body);
		String respStr = sendPost("https://api.weixin.qq.com/wxa/release?access_token=" + token,"{}");
		return JsonUtil.asObject(respStr, MiniProBaseResponse.class);
	}
	
	public  String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }    

	public MiniProDetailInfoVo getMiniProDetail(String userId, String shopId) {
		MiniProDetailInfoVo vo = new MiniProDetailInfoVo();
		ShopMiniProEntity e = repo.getByAppId(shopId);
		if (e == null) {
			return null;
		}
		BeanUtils.copyProperties(e, vo);
		MiniProAuthorizerInfo authInfo = e.getAuthorizerInfo();
		List<String> funList = new ArrayList<String>();
		vo.setFunctionList(funList);
		if (authInfo != null) {
			List<MiniProFunctionInfo> funInfoList = authInfo.getFunctionInfoList();
			if (!Collections.isEmpty(funList)) {
				for (MiniProFunctionInfo funInfo : funInfoList) {
					funList.add(funInfo.getCategory().getName());
				}
			}
		}
		return vo;
	}


	public String generateLink(String userId,String type) {
		String reidrectUrl = null;
		if("1".equals(type)){
			reidrectUrl = authRedirectUrl;
		}else if("2".equals(type)){
			reidrectUrl = miniRedirectUrl;
		}else{
			return reidrectUrl;
		}
		LOG.info("引导认证的userId"+userId+"公众平台的类型+"+type);
		LOG.info("引导授权预授权码为"+this.getPreAuthCodeValue());
		return MessageFormat.format("https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid={0}&pre_auth_code={1}&redirect_uri={2}&auth_type={3}",
				this.appId, this.getPreAuthCodeValue(), reidrectUrl + "?userId="+userId,type);
	}
	


/*	
	10、修改小程序线上代码的可见状态（仅供第三方代小程序调用）
	请求方式: POST（请使用https协议）
	https://api.weixin.qq.com/wxa/change_visitstatus?access_token=TOKEN
	
	POST数据示例:
	{
	     "action"="close"
	}
	
	参数说明:
	access_token	请使用第三方平台获取到的该小程序授权的authorizer_access_token
	action	设置可访问状态，发布后默认可访问，close为不可见，open为可见
	
	返回说明（正常时返回的json示例）：
	{
		"errcode":0,
		"errmsg":"ok",
	}
	
	错误码说明：
	返回码	说明
	-1	系统繁忙
	85021	状态不可变
	85022	action非法	
*/
	
	
	
	
/*==============代码模板管理=====================*/
	/**
	 * 1、获取草稿箱内的所有临时代码草稿
	 * @param token
	 * @return
	 */
	public MiniProDraftOrTemplateListResponse getTemplateDraftListFromWeixin(String token) {
		String resp = get("https://api.weixin.qq.com/wxa/gettemplatedraftlist?access_token=" + token);
		LOG.info("获取草稿箱的返回结果"+resp);
		return JsonUtil.asObject(resp, MiniProDraftOrTemplateListResponse.class);
	}
	

	/**
	 * 2、获取代码模版库中的所有小程序代码模版(这个Token是第三方平台的token)
	 * @param token
	 * @return
	 */
	public MiniProDraftOrTemplateListResponse getTemplateListFromWeixin() {
		String accessToken = redis.get(KEY_PREFIX, KEY_TOKEN_OBJ);
		String resp = get("https://api.weixin.qq.com/wxa/gettemplatelist?access_token=" + accessToken);
		LOG.info("获取服务器模板库返回结果"+resp);
		return JsonUtil.asObject(resp, MiniProDraftOrTemplateListResponse.class);
	}
	

	/**
	 * 3、将草稿箱的草稿选为小程序代码模版
	 * @param token
	 * @param draftId
	 * @return
	 */
	public  MiniProBaseResponse addTemplate2Weixin(String token, Long draftId) {
		Map<String, Object> body = new HashMap<String, Object>();
		body.put("draft_id", draftId);
		String resp = post("https://api.weixin.qq.com/wxa/addtotemplate?access_token=" + token, body);
		return JsonUtil.asObject(resp, MiniProBaseResponse.class);
	}

	/**
	 * 4、删除指定小程序代码模版
	 * @param token
	 * @param templateId
	 * @return
	 */
	public MiniProBaseResponse deleteTemplateFromWeixin(String token, Long templateId) {
		Map<String, Object> body = new HashMap<String, Object>();
		body.put("template_id", templateId);
		String resp = post("https://api.weixin.qq.com/wxa/addtotemplate?access_token=" + token, body);
		return JsonUtil.asObject(resp, MiniProBaseResponse.class);
	}
}
